import json
import os
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from import_export import resources, widgets
from base import models
from django.http import HttpResponse,JsonResponse
from django.template.loader import render_to_string
from django import forms
from django.apps import apps
from django.shortcuts import redirect,render
from django.contrib import messages
from django.urls import path
from django.contrib.admin.actions import delete_selected
from django.utils.translation import gettext_lazy as _
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
from django.db.models.signals import post_delete
from django.dispatch import receiver
from django.conf import settings
from financial import models as finacialModels
from django.db.models import F, ExpressionWrapper, DecimalField
from django.db.models import Sum
from decimal import Decimal, ROUND_HALF_UP
from django.contrib.admin.models import LogEntry, CHANGE
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import force_str
from django.template import RequestContext  # 关键：引入RequestContext
# Register your models here.

admin.site.site_header = '''
玄 鸟 健 康   
<br>JHX HEALTHFOOD
'''  # 设置header
admin.site.site_title = 'myErp'
admin.site.index_title = '玄鸟健康'


class DetailedLogAdmin(admin.ModelAdmin):
    """带详细字段变更日志的 Admin 基类"""

    def change_view(self, request, object_id, form_url='', extra_context=None):
        """重写修改视图，在处理表单前保存原始对象"""
        # 1. 获取原始对象（从数据库读取，未被修改的版本）
        self.original_obj = self.model.objects.get(pk=object_id)
        # 2. 调用父类方法处理正常的修改流程
        return super().change_view(request, object_id, form_url, extra_context)

    def log_change(self, request, object, message):
        # 从 change_view 中获取提前保存的原始对象
        original_obj = getattr(self, 'original_obj', None)
        if not original_obj:
            # 极端情况：如果原始对象未获取到，从数据库重新查询
            original_obj = self.model.objects.get(pk=object.pk)

        change_details = []
        for field in self.model._meta.get_fields():
            # 只处理普通可编辑字段
            if hasattr(field, 'attname') and field.editable:
                try:
                    field_name = force_str(field.verbose_name) if field.verbose_name else field.name
                    # 原始值（从提前保存的 original_obj 中获取）
                    original_value = force_str(getattr(original_obj, field.attname, ''))
                    # 新值（从修改后的 object 中获取）
                    new_value = force_str(getattr(object, field.attname, ''))

                    if original_value != new_value:
                        change_details.append(
                            f"{field_name}: 从「{original_value}」改为「{new_value}」"
                        )
                except Exception as e:
                    continue  # 忽略异常字段

        # 生成日志消息
        if change_details:
            message = {"changed": {"fields": change_details}}
        else:
            message = {"changed": {"fields": ["无实际字段变更"]}}

        return super().log_change(request, object, message)

class DefineImportExportModelAdmin(ImportExportModelAdmin,DetailedLogAdmin):
    # 指定自定义模板
    # change_form_template = 'admin/change_form.html'

    def get_urls(self):
        urls = super().get_urls()
        my_urls = [
            path(
                "print/",
                self.admin_site.admin_view(self.print_action),
                name="%s_%s_print" % self.get_model_info(),
            ),
            path(
                "<path:object_id>/change/print/",
                self.admin_site.admin_view(self.print_one_action),
                name="%s_%s_printone" % self.get_model_info(),
            ),
        ]
        return my_urls + urls
    def print_action(self, request):
        """
        Handles the default workflow for both the export form and the
        export of data to file.
        """
        queryset = self.get_export_queryset(request)
        code=self.model._meta.label
        reports=models.ReportFileModel.objects.filter(code=code,type='1',state='1').order_by('-default').all()
        context = {
            'objects': queryset,
            'title': self.model._meta.verbose_name,
            'reports':reports,
            'model':code,
            'report_type':'1'
        }
        # 渲染打印模板
        # html = render_to_string('admin/print.html', context)

        # return HttpResponse([(k.name,k.code) for k in queryset])
        # return HttpResponse(html)
        # 显式指定使用RequestContext（Django 4.x中render默认已用，但保险起见可加）
        # context_instance = RequestContext(request)
        return render(request,'admin/print.html',context)
    def print_one_action(self, request,object_id):
        """
        Handles the default workflow for both the export form and the
        export of data to file.
        """

        parms=request.POST

        # queryset=self.model.objects.filter(pk=object_id)
        # 获取必要的报表数据
        code = self.model._meta.label
        reports = models.ReportFileModel.objects.filter(
            code=code,
            type='0',
            state='1'
        ).order_by('-default').all()
        context = {
            'objects': [],
            'title': self.model._meta.verbose_name,
            'reports': reports,
             'model': code,
            'parms':parms,
            'report_type':'0'
        }
        return render(request, 'admin/print.html', context)

    # 1. 在列表页添加"打印选中"按钮
    actions = ['print_selected']

    def print_selected(self, request, queryset):
        """打印选中的记录"""
        # 仅在有选中项时处理，否则直接返回
        if not queryset:
            return None  # 无选中项时不做任何操作和提示
        code = self.model._meta.label
        reports = models.ReportFileModel.objects.filter(code=code, type='1', state='1').order_by('-default').all()
        context = {
            'objects': queryset,
            'title': self.model._meta.verbose_name,
            'reports': reports,
            'model': code

        }

        return render(request, 'admin/print.html', context)
    print_selected.short_description = "打印选中的记录"
class RawModelResource(resources.ModelResource):
    class Meta:
        model = models.RawModel

    fields = ('id', 'name', 'code', 'jian_pin', 'quan_pin', 'state')

@admin.register(models.RawModel)
class RawModelAdmin(DefineImportExportModelAdmin):
    resource_class = RawModelResource
    '''
    定义在模型列表页中显示的字段，默认只显示模型的__str__方法返回值（通常是模型的字符串表示，如名称）。
    可自定义为字段名列表，如['name', 'create_time']。
   '''
    # 动态获取所有字段（排除id）

    # list_display = [field.name for field in models.RawModel._meta.get_fields() if( field.name != 'id' and field.name != 'suppliers') ]
    # print(list_display)
    list_display =['code', 'name', 'jian_pin','gui_ge' , 'update_at','store_details_count','remark']

    # 自定义方法：通过related_name查询关联的入库明细
    def store_details_count(self, obj):
        return obj.store_details.count()  # 使用related_name='store_details'

    store_details_count.short_description = "入库记录数"
    '''    
    定义列表页中哪些字段可点击进入详情编辑页，默认是空元组（即只有list_display中的第一个字段可点击）。
    例如设置('name', 'rawmodel_id')，则列表中的name和id字段都会成为链接。

    '''
    list_display_links = ('name','code')

    '''    
    定义在列表页右侧可用于过滤数据的字段，默认无过滤条件。
    例如设置['state', 'create_at']，则会显示按状态和创建时间过滤的选项。
'''
    list_filter = ['state', 'create_at']
    '''    
    控制是否使用select_related优化关联查询（用于外键关联），默认关闭。
    设为True可减少数据库查询次数，提高列表页加载速度（适用于有外键关联的模型）。
    '''
    list_select_related = False

    '''定义列表页每页显示的记录数量，默认 100 条。  '''
    list_per_page = 10
    '''定义 “显示全部” 功能最多可显示的记录数，默认 200 条（超过此数则不显示 “显示全部” 选项）。 simpleui 不支持   '''
    list_max_show_all = 200
    ''' 
    定义列表页中可直接编辑的字段（无需进入详情页），默认无。
    例如设置['state']，则列表中的state字段可直接修改并保存。
   '''
    list_editable = ()

    '''
    定义可用于搜索的字段，默认无搜索功能。
    例如设置['name', 'code']，则会显示搜索框，可通过名称或代码搜索记录。

    '''
    search_fields = ('name',)
    # 关键配置：启用外键字段的自动完成搜索
    autocomplete_fields = ['standard']  # 对standard外键启用搜索
    '''
    定义搜索框下方的帮助文本，默认无提示文字。
    例如设置"支持按名称或代码搜索"，则会显示该提示。
    '''
    search_help_text = None
    ''' 
    定义按日期层级导航的字段（通常是DateTimeField或DateField），默认无。
    例如设置'create_at'，则列表页顶部会显示按年 / 月 / 日筛选的导航条。
    date_hierarchy = None
'''
    date_hierarchy = 'create_at'

    '''
    控制是否显示 “另存为新对象” 按钮，默认关闭（仅显示 “保存”）。
    设为True时，编辑页面会多出一个按钮，可将当前编辑的记录另存为新记录（不修改原记录）。
    save_as = False
'''
    save_as = True
    '''当save_as=True时，控制 “另存为新对象” 后是否继续编辑新对象，默认True（继续编辑）。'''
    save_as_continue = True
    ''' 
    控制是否在页面顶部显示保存按钮，默认False（仅在页面底部显示）。
    设为True时，顶部和底部都会显示保存按钮，方便长表单操作。
    save_on_top = False
   '''
    save_on_top = True
    actions_selection_counter = True

    readonly_fields = [ 'update_at']  # 关键：将时间字段设为“只读显示”

    # # 关键：引入自定义 CSS，调整表单样式
    # class Media:
    #     # 路径格式：'simpleui_custom/custom_admin.css'（对应步骤 1 中创建的文件路径）
    #     css = {
    #         'all': ('base/simpleui_custom/base_custom_admin.css',)  # 'all' 表示所有设备都加载
    #     }
    # 如果需要在保存时将用户ID存入模型，可以重写 save_model 方法
    def save_model(self, request, obj, form, change):
        # 例如：如果模型有 user 字段，可以在这里赋值
        # obj.user_id = request.user.id
        super().save_model(request, obj, form, change)

class AdditiveModelResource(resources.ModelResource):
    class Meta:
        model = models.AdditiveModel

    fields = ('id', 'name', 'code', 'jian_pin', 'quan_pin', 'state')

@admin.register(models.AdditiveModel)
class AdditiveModelAdmin(DefineImportExportModelAdmin):
    resource_class = AdditiveModelResource
    '''
    定义在模型列表页中显示的字段，默认只显示模型的__str__方法返回值（通常是模型的字符串表示，如名称）。
    可自定义为字段名列表，如['name', 'create_time']。
   '''
    # 动态获取所有字段（排除id）

    # list_display = [field.name for field in models.RawModel._meta.get_fields() if( field.name != 'id' and field.name != 'suppliers') ]
    # print(list_display)
    list_display =['code', 'name', 'jian_pin','gui_ge' , 'update_at','store_details_count','remark']

    # 自定义方法：通过related_name查询关联的入库明细
    def store_details_count(self, obj):
        return obj.store_details.count()  # 使用related_name='store_details'

    store_details_count.short_description = "入库记录数"
    '''    
    定义列表页中哪些字段可点击进入详情编辑页，默认是空元组（即只有list_display中的第一个字段可点击）。
    例如设置('name', 'rawmodel_id')，则列表中的name和id字段都会成为链接。

    '''
    list_display_links = ('name','code')

    '''    
    定义在列表页右侧可用于过滤数据的字段，默认无过滤条件。
    例如设置['state', 'create_at']，则会显示按状态和创建时间过滤的选项。
'''
    list_filter = ['state', 'create_at']
    '''    
    控制是否使用select_related优化关联查询（用于外键关联），默认关闭。
    设为True可减少数据库查询次数，提高列表页加载速度（适用于有外键关联的模型）。
    '''
    list_select_related = False

    '''定义列表页每页显示的记录数量，默认 100 条。  '''
    list_per_page = 10
    '''定义 “显示全部” 功能最多可显示的记录数，默认 200 条（超过此数则不显示 “显示全部” 选项）。 simpleui 不支持   '''
    list_max_show_all = 200
    ''' 
    定义列表页中可直接编辑的字段（无需进入详情页），默认无。
    例如设置['state']，则列表中的state字段可直接修改并保存。
   '''
    list_editable = ()

    '''
    定义可用于搜索的字段，默认无搜索功能。
    例如设置['name', 'code']，则会显示搜索框，可通过名称或代码搜索记录。

    '''
    search_fields = ('name',)
    # 关键配置：启用外键字段的自动完成搜索
    autocomplete_fields = ['standard']  # 对standard外键启用搜索
    '''
    定义搜索框下方的帮助文本，默认无提示文字。
    例如设置"支持按名称或代码搜索"，则会显示该提示。
    '''
    search_help_text = None
    ''' 
    定义按日期层级导航的字段（通常是DateTimeField或DateField），默认无。
    例如设置'create_at'，则列表页顶部会显示按年 / 月 / 日筛选的导航条。
    date_hierarchy = None
'''
    date_hierarchy = 'create_at'

    '''
    控制是否显示 “另存为新对象” 按钮，默认关闭（仅显示 “保存”）。
    设为True时，编辑页面会多出一个按钮，可将当前编辑的记录另存为新记录（不修改原记录）。
    save_as = False
'''
    save_as = True
    '''当save_as=True时，控制 “另存为新对象” 后是否继续编辑新对象，默认True（继续编辑）。'''
    save_as_continue = True
    ''' 
    控制是否在页面顶部显示保存按钮，默认False（仅在页面底部显示）。
    设为True时，顶部和底部都会显示保存按钮，方便长表单操作。
    save_on_top = False
   '''
    save_on_top = True
    actions_selection_counter = True

    readonly_fields = [ 'update_at']  # 关键：将时间字段设为“只读显示”

    # # 关键：引入自定义 CSS，调整表单样式
    # class Media:
    #     # 路径格式：'simpleui_custom/custom_admin.css'（对应步骤 1 中创建的文件路径）
    #     css = {
    #         'all': ('base/simpleui_custom/base_custom_admin.css',)  # 'all' 表示所有设备都加载
    #     }

class PackagingModelResource(resources.ModelResource):
    class Meta:
        model = models.PackagingModel

    fields = ('id', 'name', 'code', 'jian_pin', 'quan_pin', 'state')

@admin.register(models.PackagingModel)
class PackagingModelAdmin(DefineImportExportModelAdmin):
    resource_class = PackagingModelResource
    '''
    定义在模型列表页中显示的字段，默认只显示模型的__str__方法返回值（通常是模型的字符串表示，如名称）。
    可自定义为字段名列表，如['name', 'create_time']。
   '''
    # 动态获取所有字段（排除id）

    # list_display = [field.name for field in models.RawModel._meta.get_fields() if( field.name != 'id' and field.name != 'suppliers') ]
    # print(list_display)
    list_display =['code', 'name', 'jian_pin','gui_ge' , 'update_at','store_details_count','remark']

    # 自定义方法：通过related_name查询关联的入库明细
    def store_details_count(self, obj):
        return obj.store_details.count()  # 使用related_name='store_details'

    store_details_count.short_description = "入库记录数"
    '''    
    定义列表页中哪些字段可点击进入详情编辑页，默认是空元组（即只有list_display中的第一个字段可点击）。
    例如设置('name', 'rawmodel_id')，则列表中的name和id字段都会成为链接。

    '''
    list_display_links = ('name','code')

    '''    
    定义在列表页右侧可用于过滤数据的字段，默认无过滤条件。
    例如设置['state', 'create_at']，则会显示按状态和创建时间过滤的选项。
'''
    list_filter = ['state', 'create_at']
    '''    
    控制是否使用select_related优化关联查询（用于外键关联），默认关闭。
    设为True可减少数据库查询次数，提高列表页加载速度（适用于有外键关联的模型）。
    '''
    list_select_related = False

    '''定义列表页每页显示的记录数量，默认 100 条。  '''
    list_per_page = 10
    '''定义 “显示全部” 功能最多可显示的记录数，默认 200 条（超过此数则不显示 “显示全部” 选项）。 simpleui 不支持   '''
    list_max_show_all = 200
    ''' 
    定义列表页中可直接编辑的字段（无需进入详情页），默认无。
    例如设置['state']，则列表中的state字段可直接修改并保存。
   '''
    list_editable = ()

    '''
    定义可用于搜索的字段，默认无搜索功能。
    例如设置['name', 'code']，则会显示搜索框，可通过名称或代码搜索记录。

    '''
    search_fields = ('name',)
    # 关键配置：启用外键字段的自动完成搜索
    autocomplete_fields = ['standard']  # 对standard外键启用搜索
    '''
    定义搜索框下方的帮助文本，默认无提示文字。
    例如设置"支持按名称或代码搜索"，则会显示该提示。
    '''
    search_help_text = None
    ''' 
    定义按日期层级导航的字段（通常是DateTimeField或DateField），默认无。
    例如设置'create_at'，则列表页顶部会显示按年 / 月 / 日筛选的导航条。
    date_hierarchy = None
'''
    date_hierarchy = 'create_at'

    '''
    控制是否显示 “另存为新对象” 按钮，默认关闭（仅显示 “保存”）。
    设为True时，编辑页面会多出一个按钮，可将当前编辑的记录另存为新记录（不修改原记录）。
    save_as = False
'''
    save_as = True
    '''当save_as=True时，控制 “另存为新对象” 后是否继续编辑新对象，默认True（继续编辑）。'''
    save_as_continue = True
    ''' 
    控制是否在页面顶部显示保存按钮，默认False（仅在页面底部显示）。
    设为True时，顶部和底部都会显示保存按钮，方便长表单操作。
    save_on_top = False
   '''
    save_on_top = True
    actions_selection_counter = True

    readonly_fields = [ 'update_at']  # 关键：将时间字段设为“只读显示”

    # # 关键：引入自定义 CSS，调整表单样式
    # class Media:
    #     # 路径格式：'simpleui_custom/custom_admin.css'（对应步骤 1 中创建的文件路径）
    #     css = {
    #         'all': ('base/simpleui_custom/base_custom_admin.css',)  # 'all' 表示所有设备都加载
    #     }
class IntermediateModelResource(resources.ModelResource):
    class Meta:
        model = models.IntermediateModel

    fields = ('id', 'name', 'code', 'jian_pin', 'quan_pin', 'state')

@admin.register(models.IntermediateModel)
class IntermediateModelAdmin(DefineImportExportModelAdmin):
    resource_class = IntermediateModelResource
    '''
    定义在模型列表页中显示的字段，默认只显示模型的__str__方法返回值（通常是模型的字符串表示，如名称）。
    可自定义为字段名列表，如['name', 'create_time']。
   '''
    # 动态获取所有字段（排除id）

    # list_display = [field.name for field in models.RawModel._meta.get_fields() if( field.name != 'id' and field.name != 'suppliers') ]
    # print(list_display)
    list_display =['code', 'name', 'jian_pin','gui_ge' , 'update_at','store_details_count','remark']

    # 自定义方法：通过related_name查询关联的入库明细
    def store_details_count(self, obj):
        return obj.store_details.count()  # 使用related_name='store_details'

    store_details_count.short_description = "入库记录数"
    '''    
    定义列表页中哪些字段可点击进入详情编辑页，默认是空元组（即只有list_display中的第一个字段可点击）。
    例如设置('name', 'rawmodel_id')，则列表中的name和id字段都会成为链接。

    '''
    list_display_links = ('name','code')

    '''    
    定义在列表页右侧可用于过滤数据的字段，默认无过滤条件。
    例如设置['state', 'create_at']，则会显示按状态和创建时间过滤的选项。
'''
    list_filter = ['state', 'create_at']
    '''    
    控制是否使用select_related优化关联查询（用于外键关联），默认关闭。
    设为True可减少数据库查询次数，提高列表页加载速度（适用于有外键关联的模型）。
    '''
    list_select_related = False

    '''定义列表页每页显示的记录数量，默认 100 条。  '''
    list_per_page = 10
    '''定义 “显示全部” 功能最多可显示的记录数，默认 200 条（超过此数则不显示 “显示全部” 选项）。 simpleui 不支持   '''
    list_max_show_all = 200
    ''' 
    定义列表页中可直接编辑的字段（无需进入详情页），默认无。
    例如设置['state']，则列表中的state字段可直接修改并保存。
   '''
    list_editable = ()

    '''
    定义可用于搜索的字段，默认无搜索功能。
    例如设置['name', 'code']，则会显示搜索框，可通过名称或代码搜索记录。

    '''
    search_fields = ('name',)
    # 关键配置：启用外键字段的自动完成搜索
    autocomplete_fields = ['standard']  # 对standard外键启用搜索
    '''
    定义搜索框下方的帮助文本，默认无提示文字。
    例如设置"支持按名称或代码搜索"，则会显示该提示。
    '''
    search_help_text = None
    ''' 
    定义按日期层级导航的字段（通常是DateTimeField或DateField），默认无。
    例如设置'create_at'，则列表页顶部会显示按年 / 月 / 日筛选的导航条。
    date_hierarchy = None
'''
    date_hierarchy = 'create_at'

    '''
    控制是否显示 “另存为新对象” 按钮，默认关闭（仅显示 “保存”）。
    设为True时，编辑页面会多出一个按钮，可将当前编辑的记录另存为新记录（不修改原记录）。
    save_as = False
'''
    save_as = True
    '''当save_as=True时，控制 “另存为新对象” 后是否继续编辑新对象，默认True（继续编辑）。'''
    save_as_continue = True
    ''' 
    控制是否在页面顶部显示保存按钮，默认False（仅在页面底部显示）。
    设为True时，顶部和底部都会显示保存按钮，方便长表单操作。
    save_on_top = False
   '''
    save_on_top = True
    actions_selection_counter = True

    readonly_fields = [ 'update_at']  # 关键：将时间字段设为“只读显示”

    # # 关键：引入自定义 CSS，调整表单样式
    # class Media:
    #     # 路径格式：'simpleui_custom/custom_admin.css'（对应步骤 1 中创建的文件路径）
    #     css = {
    #         'all': ('base/simpleui_custom/base_custom_admin.css',)  # 'all' 表示所有设备都加载
    #     }

class FinishedModelResource(resources.ModelResource):
    class Meta:
        model = models.FinishedModel

    fields = ('id', 'name', 'code', 'jian_pin', 'quan_pin', 'state')

@admin.register(models.FinishedModel)
class FinishedModelAdmin(DefineImportExportModelAdmin):
    resource_class = FinishedModelResource
    '''
    定义在模型列表页中显示的字段，默认只显示模型的__str__方法返回值（通常是模型的字符串表示，如名称）。
    可自定义为字段名列表，如['name', 'create_time']。
   '''
    # 动态获取所有字段（排除id）

    # list_display = [field.name for field in models.RawModel._meta.get_fields() if( field.name != 'id' and field.name != 'suppliers') ]
    # print(list_display)
    list_display =['code', 'name', 'jian_pin','gui_ge' ,'update_at','store_details_count','remark']

    # 自定义方法：通过related_name查询关联的入库明细
    def store_details_count(self, obj):
        return 0
        # return obj.store_details.count()  # 使用related_name='store_details'

    store_details_count.short_description = "入库记录数"
    '''    
    定义列表页中哪些字段可点击进入详情编辑页，默认是空元组（即只有list_display中的第一个字段可点击）。
    例如设置('name', 'rawmodel_id')，则列表中的name和id字段都会成为链接。

    '''
    list_display_links = ('name','code')

    '''    
    定义在列表页右侧可用于过滤数据的字段，默认无过滤条件。
    例如设置['state', 'create_at']，则会显示按状态和创建时间过滤的选项。
'''
    list_filter = ['state', 'create_at']
    '''    
    控制是否使用select_related优化关联查询（用于外键关联），默认关闭。
    设为True可减少数据库查询次数，提高列表页加载速度（适用于有外键关联的模型）。
    '''
    list_select_related = False

    '''定义列表页每页显示的记录数量，默认 100 条。  '''
    list_per_page = 10
    '''定义 “显示全部” 功能最多可显示的记录数，默认 200 条（超过此数则不显示 “显示全部” 选项）。 simpleui 不支持   '''
    list_max_show_all = 200
    ''' 
    定义列表页中可直接编辑的字段（无需进入详情页），默认无。
    例如设置['state']，则列表中的state字段可直接修改并保存。
   '''
    list_editable = ()

    '''
    定义可用于搜索的字段，默认无搜索功能。
    例如设置['name', 'code']，则会显示搜索框，可通过名称或代码搜索记录。

    '''
    search_fields = ('name',)
    # 关键配置：启用外键字段的自动完成搜索
    autocomplete_fields = ['standard']  # 对standard外键启用搜索
    '''
    定义搜索框下方的帮助文本，默认无提示文字。
    例如设置"支持按名称或代码搜索"，则会显示该提示。
    '''
    search_help_text = None
    ''' 
    定义按日期层级导航的字段（通常是DateTimeField或DateField），默认无。
    例如设置'create_at'，则列表页顶部会显示按年 / 月 / 日筛选的导航条。
    date_hierarchy = None
'''
    date_hierarchy = 'create_at'

    '''
    控制是否显示 “另存为新对象” 按钮，默认关闭（仅显示 “保存”）。
    设为True时，编辑页面会多出一个按钮，可将当前编辑的记录另存为新记录（不修改原记录）。
    save_as = False
'''
    save_as = True
    '''当save_as=True时，控制 “另存为新对象” 后是否继续编辑新对象，默认True（继续编辑）。'''
    save_as_continue = True
    ''' 
    控制是否在页面顶部显示保存按钮，默认False（仅在页面底部显示）。
    设为True时，顶部和底部都会显示保存按钮，方便长表单操作。
    save_on_top = False
   '''
    save_on_top = True
    actions_selection_counter = True

    readonly_fields = [ 'update_at']  # 关键：将时间字段设为“只读显示”

    # # 关键：引入自定义 CSS，调整表单样式
    # class Media:
    #     # 路径格式：'simpleui_custom/custom_admin.css'（对应步骤 1 中创建的文件路径）
    #     css = {
    #         'all': ('base/simpleui_custom/base_custom_admin.css',)  # 'all' 表示所有设备都加载
    #     }

@admin.register(models.StandardModel)
class StandardModelAdmin(admin.ModelAdmin):
    # 关键：为StandardModel添加search_fields，供autocomplete_fields使用
    search_fields = ('name',)  # 必须定义，否则关联的autocomplete会报错
    list_display = ('name', 'create_at', 'update_at')
# admin.site.register(models.StandardModel)

@admin.register(models.SupplierRawModel)
class SupplierRawModelAdmin(DefineImportExportModelAdmin):
    list_display = ('name',  'display_raws','remark','invoice','total_num','un_finished_invoice','payment','un_finished_payment')  # 自定义显示多对多字段
    filter_horizontal = ('raws',)  # 水平筛选器，便于选择多对多关系
    search_fields=['name']
    # 自定义方法：显示书籍的所有作者
    def display_raws(self, obj):
        result = ", ".join([raw.name for raw in obj.raws.all()])
        if len(result) > 20:
            return result[:20] + '...'
        else:
            return result
    display_raws.short_description = "供应的原料列表"  # 列标题


    def invoice(self, obj):
        invoice_num = 0
        # 获取所有入库单号
        stores = obj.storerawordermodel_set.all()
        # 获取所有已开发票信息
        invoiceraws = finacialModels.InvoiceRawModel.objects.filter(store_orders__in=stores).all()
        for invoiceraw in set(invoiceraws):
            invoice_num += invoiceraw.amount
        return invoice_num


    invoice.short_description = "已开发票"  # 列标题


    def total_num(self, obj):
        _num = Decimal('0.00')
        # 获取所有入库单号
        stores = obj.storerawordermodel_set.all()
        for store in stores:
            total = store.storerawdetailmodel_set.filter(parent__isnull=True).aggregate(
                total=Sum(
                    ExpressionWrapper(
                        F('price') * F('store_num') * (1 + F('tax') * 0.01),  # 正确的字段计算方式
                        output_field=DecimalField()
                    )
                )
            )['total'] or 0
            # 确保total是Decimal类型（如果为None则转为0.00）
            if total is None:
                total = Decimal('0.00')
            else:
                total = Decimal(str(total))
            _num += total
        return _num.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)


    total_num.short_description = "总含税金额"  # 列标题


    def un_finished_invoice(self, obj):
        return self.total_num(obj) - self.invoice(obj)


    un_finished_invoice.short_description = "未开发票金额"  # 列标题
    def payment(self, obj):
        invoice_num = 0
        # 获取所有付款
        payments = obj.paymentrawmodel_set.all()
        for pay in payments:
            invoice_num += pay.amount
        return invoice_num
    payment.short_description = "已付款"  # 列标题
    def un_finished_payment(self, obj):
        return self.total_num(obj) - self.payment(obj)


    un_finished_payment.short_description = "未付款"  # 列标题
@admin.register(models.SupplierAdditiveModel)
class SupplierAdditiveModelAdmin(DefineImportExportModelAdmin):
    list_display = ('name',  'display_raws','remark','invoice','total_num','un_finished_invoice','payment','un_finished_payment')  # 自定义显示多对多字段
    filter_horizontal = ('raws',)  # 水平筛选器，便于选择多对多关系
    search_fields=['name']
    # 自定义方法：显示书籍的所有作者
    def display_raws(self, obj):
        result = ", ".join([raw.name for raw in obj.raws.all()])
        if len(result) > 20:
            return result[:20] + '...'
        else:
            return result

    display_raws.short_description = "供应的添加剂列表"  # 列标题

    def invoice(self, obj):
        invoice_num = 0
        # 获取所有入库单号
        stores = obj.storeadditiveordermodel_set.all()
        # 获取所有已开发票信息
        invoicepackagings = finacialModels.InvoiceAdditiveModel.objects.filter(store_orders__in=stores).all()
        for invoicepackaging in set(invoicepackagings):
            invoice_num += invoicepackaging.amount
        return invoice_num

    invoice.short_description = "已开发票"  # 列标题

    def total_num(self, obj):
        _num = Decimal('0.00')
        # 获取所有入库单号
        stores = obj.storeadditiveordermodel_set.all()
        for store in stores:
            total = store.storeadditivedetailmodel_set.filter(parent__isnull=True).aggregate(
                total=Sum(
                    ExpressionWrapper(
                        F('price') * F('store_num') * (1 + F('tax') * 0.01),  # 正确的字段计算方式
                        output_field=DecimalField()
                    )
                )
            )['total'] or 0
            # 确保total是Decimal类型（如果为None则转为0.00）
            if total is None:
                total = Decimal('0.00')
            else:
                total = Decimal(str(total))
            _num += total
        return _num.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)

    total_num.short_description = "总含税金额"  # 列标题

    def un_finished_invoice(self, obj):
        return self.total_num(obj) - self.invoice(obj)

    un_finished_invoice.short_description = "未开发票金额"  # 列标题
    def payment(self, obj):
        invoice_num = 0
        # 获取所有付款
        payments = obj.paymentadditivemodel_set.all()
        for pay in payments:
            invoice_num += pay.amount
        return invoice_num
    payment.short_description = "已付款"  # 列标题
    def un_finished_payment(self, obj):
        return self.total_num(obj) - self.payment(obj)


    un_finished_payment.short_description = "未付款"  # 列标题
@admin.register(models.SupplierPackagingModel)
class SupplierPackagingModelAdmin(DefineImportExportModelAdmin):
    list_display = ('name',  'display_raws','remark','invoice','total_num','un_finished_invoice','payment','un_finished_payment')  # 自定义显示多对多字段
    filter_horizontal = ('raws',)  # 水平筛选器，便于选择多对多关系
    search_fields=['name']
    # 自定义方法：显示书籍的所有作者
    def display_raws(self, obj):
        result= ", ".join([raw.name for raw in obj.raws.all()])
        if len(result)>20:
            return result[:20]+'...'
        else:
            return result
    display_raws.short_description = "供应的包材列表"  # 列标题

    def invoice(self, obj):
        invoice_num=0
        #获取所有入库单号
        stores=obj.storepackagingordermodel_set.all()
        #获取所有已开发票信息
        invoicepackagings=finacialModels.InvoicePackagingModel.objects.filter(store_orders__in=stores).all()
        for invoicepackaging in set(invoicepackagings):
            invoice_num += invoicepackaging.amount
        return invoice_num
    invoice.short_description = "已开发票"  # 列标题
    def total_num(self, obj):
        _num=Decimal('0.00')
        #获取所有入库单号
        stores=obj.storepackagingordermodel_set.all()
        for store in stores:
            total = store.storepackagingdetailmodel_set.filter(parent__isnull=True).aggregate(
                total=Sum(
                    ExpressionWrapper(
                        F('price') * F('store_num') * (1 + F('tax')*0.01),  # 正确的字段计算方式
                        output_field=DecimalField()
                    )
                )
            )['total'] or 0
            # 确保total是Decimal类型（如果为None则转为0.00）
            if total is None:
                total = Decimal('0.00')
            else:
                total = Decimal(str(total))
            _num+=total
        return _num.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
    total_num.short_description = "总含税金额"  # 列标题
    def un_finished_invoice(self,obj):
        return self.total_num(obj)-self.invoice(obj)

    un_finished_invoice.short_description = "未开发票金额"  # 列标题
    def payment(self, obj):
        invoice_num = 0
        # 获取所有付款
        payments = obj.paymentpackagingmodel_set.all()
        for pay in payments:
            invoice_num += pay.amount
        return invoice_num
    payment.short_description = "已付款"  # 列标题
    def un_finished_payment(self, obj):
        return self.total_num(obj) - self.payment(obj)


    un_finished_payment.short_description = "未付款"  # 列标题
@admin.register(models.CustomerRawModel)
class CustomerRawModelAdmin(DefineImportExportModelAdmin):
    list_display = ('name',  'display_raws')  # 自定义显示多对多字段
    filter_horizontal = ('raws',)  # 水平筛选器，便于选择多对多关系
    search_fields=['name']
    # 自定义方法：显示书籍的所有作者
    def display_raws(self, obj):
        return ", ".join([raw.name for raw in obj.raws.all()])
    display_raws.short_description = "销售的原料列表"  # 列标题

    # # 2. 核心：控制自动完成的搜索结果，只返回启用的供应商
    # def get_search_results(self, request, queryset, search_term):
    #     # 先执行父类的搜索逻辑（处理用户输入的搜索关键词）
    #     queryset, use_distinct = super().get_search_results(request, queryset, search_term)
    #
    #     # 叠加过滤条件：只保留启用的供应商（state='1'）
    #     queryset = queryset.filter(state='1')
    #
    #     return queryset, use_distinct
@admin.register(models.CustomerFinishedModel)
class CustomerFinishedModelAdmin(DefineImportExportModelAdmin):
    list_display = ('name',  'display_raws')  # 自定义显示多对多字段
    filter_horizontal = ('raws',)  # 水平筛选器，便于选择多对多关系
    search_fields=['name']
    # 自定义方法：显示书籍的所有作者
    def display_raws(self, obj):
        return ", ".join([raw.name for raw in obj.raws.all()])
    display_raws.short_description = "销售的成品列表"  # 列标题

    # # 2. 核心：控制自动完成的搜索结果，只返回启用的供应商
    # def get_search_results(self, request, queryset, search_term):
    #     # 先执行父类的搜索逻辑（处理用户输入的搜索关键词）
    #     queryset, use_distinct = super().get_search_results(request, queryset, search_term)
    #
    #     # 叠加过滤条件：只保留启用的供应商（state='1'）
    #     queryset = queryset.filter(state='1')
    #
    #     return queryset, use_distinct
@admin.register(models.CustomerPackagingModel)
class CustomerPackagingModelAdmin(DefineImportExportModelAdmin):
    list_display = ('name',  'display_raws')  # 自定义显示多对多字段
    filter_horizontal = ('raws',)  # 水平筛选器，便于选择多对多关系
    search_fields=['name']
    # 自定义方法：显示书籍的所有作者
    def display_raws(self, obj):
        return ", ".join([raw.name for raw in obj.raws.all()])
    display_raws.short_description = "销售的包材列表"  # 列标题

@admin.register(models.CustomerAdditiveModel)
class CustomerAdditiveModelAdmin(DefineImportExportModelAdmin):
    list_display = ('name',  'display_raws')  # 自定义显示多对多字段
    filter_horizontal = ('raws',)  # 水平筛选器，便于选择多对多关系
    search_fields=['name']
    # 自定义方法：显示书籍的所有作者
    def display_raws(self, obj):
        return ", ".join([raw.name for raw in obj.raws.all()])
    display_raws.short_description = "销售的添加剂列表"  # 列标题
@admin.register(models.CustomerIntermediateModel)
class CustomerIntermediateModelAdmin(DefineImportExportModelAdmin):
    list_display = ('name',  'display_raws')  # 自定义显示多对多字段
    filter_horizontal = ('raws',)  # 水平筛选器，便于选择多对多关系
    search_fields=['name']
    # 自定义方法：显示书籍的所有作者
    def display_raws(self, obj):
        return ", ".join([raw.name for raw in obj.raws.all()])
    display_raws.short_description = "销售的中间品列表"  # 列标题

@admin.register(models.UnitModel)
class UnitModelAdmin(DefineImportExportModelAdmin):
    # 关键：为StandardModel添加search_fields，供autocomplete_fields使用
    search_fields = ('unit',)  # 必须定义，否则关联的autocomplete会报错
    list_display = ('unit','unit_rel','aux_unit', 'create_at', 'update_at')
    ordering = ['id']  # 若需要降序，可写成['-id']
import importlib
class ReportFileModelForm(forms.ModelForm):


    # 获取所有模型并创建选项
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # 获取所有模型，格式为 (app_label.model_name, 模型显示名)
        model_choices = []
        for model in apps.get_models():
            # 构建选项值，格式为 "app_label.model_name"
            value = f"{model._meta.app_label}.{model.__name__}"
            # 显示文本，使用模型的 verbose_name
            label = f"{model._meta.app_label} - {model._meta.verbose_name}"
            model_choices.append((value, label))
        #抽象类模型
        abstract_models=(('base.BaseModel','base - 所有物料'),('base.SupplierModel','base - 所有供应商'),
                          ('base.CustomerModel', 'base - 所有客户'), ('base.LocationModel', 'base - 所有货位'),
                         )
        for model in abstract_models:
            # 构建选项值，格式为 "app_label.model_name"
            value = model[0]
            # 显示文本，使用模型的 verbose_name
            label = model[1]
            model_choices.append((value, label))

        model_choices.sort(key=lambda k :k[1])
        # 将字段设置为选择框并赋值选项
        self.fields['code'] = forms.ChoiceField(
            choices=model_choices,
            label="选择标识"
        )

    class Meta:
        model = models.ReportFileModel
        fields = [ 'code', 'type', 'state','default','remarks','report_file',]  # 或者指定需要的字段


@admin.register(models.ReportFileModel)
class ReportFileModelAdmin(DefineImportExportModelAdmin):
    form = ReportFileModelForm
    list_display = ('report_name','report_file', 'code', 'type', 'state','default')
    search_fields = ('report_name',)
    list_filter = ('report_name',)

    # 如果需要在保存时将用户ID存入模型，可以重写 save_model 方法
    def save_model(self, request, obj, form, change):
        # 例如：如果模型有 user 字段，可以在这里赋值
        obj.user_id = request.user.id
        super().save_model(request, obj, form, change)

# 定义信号接收器：在模型实例被删除后触发
@receiver(post_delete, sender=models.ReportFileModel)
def delete_report_file(sender, instance,** kwargs):
    """删除模型记录时，同时删除对应的附件文件"""
    if instance.report_file:
        file_path = os.path.join(settings.MEDIA_ROOT, str(instance.report_file))
        if os.path.exists(file_path):
            os.remove(file_path)

@admin.register(models.LocationRawModel)
class LocationRawModelAdmin(DefineImportExportModelAdmin):
    # 关键：为StandardModel添加search_fields，供autocomplete_fields使用
    search_fields = ('name','code',)  # 必须定义，否则关联的autocomplete会报错
    list_display = ('name','code','type', 'create_at', 'update_at')

@admin.register(models.LocationAdditiveModel)
class LocationAdditiveModelAdmin(DefineImportExportModelAdmin):
    # 关键：为StandardModel添加search_fields，供autocomplete_fields使用
    search_fields = ('name','code',)  # 必须定义，否则关联的autocomplete会报错
    list_display = ('name','code','type', 'create_at', 'update_at')

@admin.register(models.LocationPackagingModel)
class LocationPackagingModelAdmin(DefineImportExportModelAdmin):
    # 关键：为StandardModel添加search_fields，供autocomplete_fields使用
    search_fields = ('name','code',)  # 必须定义，否则关联的autocomplete会报错
    list_display = ('name','code','type', 'create_at', 'update_at')
@admin.register(models.LocationIntermediateModel)
class LocationIntermediateModelAdmin(DefineImportExportModelAdmin):
    # 关键：为StandardModel添加search_fields，供autocomplete_fields使用
    search_fields = ('name','code',)  # 必须定义，否则关联的autocomplete会报错
    list_display = ('name','code','type', 'create_at', 'update_at')
@admin.register(models.LocationFinishedModel)
class LocationFinshedModelAdmin(DefineImportExportModelAdmin):
    # 关键：为StandardModel添加search_fields，供autocomplete_fields使用
    search_fields = ('name','code',)  # 必须定义，否则关联的autocomplete会报错
    list_display = ('name','code','type', 'create_at', 'update_at')